1 package com.nexes.wizard;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import java.beans.*;
6 import java.util.*;
7 import java.net.*;
8
9 import javax.swing.*;
10 import javax.swing.border.*;
11
12 /***
13 * This class implements a basic wizard dialog, where the programmer can
14 * insert one or more Components to act as panels. These panels can be navigated
15 * through arbitrarily using the 'Next' or 'Back' buttons, or the dialog itself
16 * can be closed using the 'Cancel' button. Note that even though the dialog
17 * uses a CardLayout manager, the order of the panels is not linear. Each panel
18 * determines at runtime what its next and previous panel will be.
19 */
20 public class Wizard extends WindowAdapter implements PropertyChangeListener {
21
22 /***
23 * Indicates that the 'Finish' button was pressed to close the dialog.
24 */
25 public static final int FINISH_RETURN_CODE = 0;
26 /***
27 * Indicates that the 'Cancel' button was pressed to close the dialog, or
28 * the user pressed the close box in the corner of the window.
29 */
30 public static final int CANCEL_RETURN_CODE = 1;
31 /***
32 * Indicates that the dialog closed due to an internal error.
33 */
34 public static final int ERROR_RETURN_CODE = 2;
35
36 /***
37 * The String-based action command for the 'Next' button.
38 */
39 public static final String NEXT_BUTTON_ACTION_COMMAND = "NextButtonActionCommand";
40 /***
41 * The String-based action command for the 'Back' button.
42 */
43 public static final String BACK_BUTTON_ACTION_COMMAND = "BackButtonActionCommand";
44 /***
45 * The String-based action command for the 'Cancel' button.
46 */
47 public static final String CANCEL_BUTTON_ACTION_COMMAND = "CancelButtonActionCommand";
48
49
50
51
52 static String BACK_TEXT;
53 static String NEXT_TEXT;
54 static String FINISH_TEXT;
55 static String CANCEL_TEXT;
56
57
58
59 static Icon BACK_ICON;
60 static Icon NEXT_ICON;
61 static Icon FINISH_ICON;
62 static Icon CANCEL_ICON;
63
64
65 private WizardModel wizardModel;
66 private WizardController wizardController;
67 private JDialog wizardDialog;
68
69 private JPanel cardPanel;
70 private CardLayout cardLayout;
71 private JButton backButton;
72 private JButton nextButton;
73 private JButton cancelButton;
74
75 private int returnCode;
76
77
78
79 /***
80 * Default constructor. This method creates a new WizardModel object and passes it
81 * into the overloaded constructor.
82 */
83 public Wizard() {
84 this((Frame)null);
85 }
86
87 /***
88 * This method accepts a java.awt.Dialog object as the javax.swing.JDialog's
89 * parent.
90 * @param owner The java.awt.Dialog object that is the owner of this dialog.
91 */
92 public Wizard(Dialog owner) {
93 wizardModel = new WizardModel();
94 wizardDialog = new JDialog(owner);
95 initComponents();
96 }
97
98 /***
99 * This method accepts a java.awt.Frame object as the javax.swing.JDialog's
100 * parent.
101 * @param owner The java.awt.Frame object that is the owner of the javax.swing.JDialog.
102 */
103 public Wizard(Frame owner) {
104 wizardModel = new WizardModel();
105 wizardDialog = new JDialog(owner);
106 initComponents();
107 }
108
109 /***
110 * Returns an instance of the JDialog that this class created. This is useful in
111 * the event that you want to change any of the JDialog parameters manually.
112 * @return The JDialog instance that this class created.
113 */
114 public JDialog getDialog() {
115 return wizardDialog;
116 }
117
118 /***
119 * Returns the owner of the generated javax.swing.JDialog.
120 * @return The owner (java.awt.Frame or java.awt.Dialog) of the javax.swing.JDialog generated
121 * by this class.
122 */
123 public Component getOwner() {
124 return wizardDialog.getOwner();
125 }
126
127 /***
128 * Sets the title of the generated javax.swing.JDialog.
129 * @param s The title of the dialog.
130 */
131 public void setTitle(String s) {
132 wizardDialog.setTitle(s);
133 }
134
135 /***
136 * Returns the current title of the generated dialog.
137 * @return The String-based title of the generated dialog.
138 */
139 public String getTitle() {
140 return wizardDialog.getTitle();
141 }
142
143 /***
144 * Sets the modality of the generated javax.swing.JDialog.
145 * @param b the modality of the dialog
146 */
147 public void setModal(boolean b) {
148 wizardDialog.setModal(b);
149 }
150
151 /***
152 * Returns the modality of the dialog.
153 * @return A boolean indicating whether or not the generated javax.swing.JDialog is modal.
154 */
155 public boolean isModal() {
156 return wizardDialog.isModal();
157 }
158
159 /***
160 * Convienence method that displays a modal wizard dialog and blocks until the dialog
161 * has completed.
162 * @return Indicates how the dialog was closed. Compare this value against the RETURN_CODE
163 * constants at the beginning of the class.
164 */
165 public int showModalDialog() {
166
167 wizardDialog.setModal(true);
168 wizardDialog.pack();
169 wizardDialog.setVisible(true);
170
171 return returnCode;
172 }
173
174 /***
175 * Returns the current model of the wizard dialog.
176 * @return A WizardModel instance, which serves as the model for the wizard dialog.
177 */
178 public WizardModel getModel() {
179 return wizardModel;
180 }
181
182 /***
183 * Add a Component as a panel for the wizard dialog by registering its
184 * WizardPanelDescriptor object. Each panel is identified by a unique Object-based
185 * identifier (often a String), which can be used by the setCurrentPanel()
186 * method to display the panel at runtime.
187 * @param id An Object-based identifier used to identify the WizardPanelDescriptor object.
188 * @param panel The WizardPanelDescriptor object which contains helpful information about the panel.
189 */
190 public void registerWizardPanel(Object id, WizardPanelDescriptor panel) {
191
192
193
194
195 cardPanel.add(panel.getPanelComponent(), id);
196
197
198
199 panel.setWizard(this);
200
201
202
203 wizardModel.registerPanel(id, panel);
204
205 }
206
207 /***
208 * Displays the panel identified by the object passed in. This is the same Object-based
209 * identified used when registering the panel.
210 * @param id The Object-based identifier of the panel to be displayed.
211 */
212 public void setCurrentPanel(Object id) {
213
214
215
216
217
218 if (id == null)
219 close(ERROR_RETURN_CODE);
220
221 WizardPanelDescriptor oldPanelDescriptor = wizardModel.getCurrentPanelDescriptor();
222 if (oldPanelDescriptor != null)
223 oldPanelDescriptor.aboutToHidePanel();
224
225 wizardModel.setCurrentPanel(id);
226 wizardModel.getCurrentPanelDescriptor().aboutToDisplayPanel();
227
228
229
230 cardLayout.show(cardPanel, id.toString());
231 wizardModel.getCurrentPanelDescriptor().displayingPanel();
232
233
234 }
235
236 /***
237 * Method used to listen for property change events from the model and update the
238 * dialog's graphical components as necessary.
239 * @param evt PropertyChangeEvent passed from the model to signal that one of its properties has changed value.
240 */
241 public void propertyChange(PropertyChangeEvent evt) {
242
243 if (evt.getPropertyName().equals(WizardModel.CURRENT_PANEL_DESCRIPTOR_PROPERTY)) {
244 wizardController.resetButtonsToPanelRules();
245 } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_TEXT_PROPERTY)) {
246 nextButton.setText(evt.getNewValue().toString());
247 } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_TEXT_PROPERTY)) {
248 backButton.setText(evt.getNewValue().toString());
249 } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_TEXT_PROPERTY)) {
250 cancelButton.setText(evt.getNewValue().toString());
251 } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ENABLED_PROPERTY)) {
252 nextButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
253 } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ENABLED_PROPERTY)) {
254 backButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
255 } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ENABLED_PROPERTY)) {
256 cancelButton.setEnabled(((Boolean)evt.getNewValue()).booleanValue());
257 } else if (evt.getPropertyName().equals(WizardModel.NEXT_FINISH_BUTTON_ICON_PROPERTY)) {
258 nextButton.setIcon((Icon)evt.getNewValue());
259 } else if (evt.getPropertyName().equals(WizardModel.BACK_BUTTON_ICON_PROPERTY)) {
260 backButton.setIcon((Icon)evt.getNewValue());
261 } else if (evt.getPropertyName().equals(WizardModel.CANCEL_BUTTON_ICON_PROPERTY)) {
262 cancelButton.setIcon((Icon)evt.getNewValue());
263 }
264
265 }
266
267 /***
268 * Retrieves the last return code set by the dialog.
269 * @return An integer that identifies how the dialog was closed. See the *_RETURN_CODE
270 * constants of this class for possible values.
271 */
272 public int getReturnCode() {
273 return returnCode;
274 }
275
276 /***
277 * Mirrors the WizardModel method of the same name.
278 * @return A boolean indicating if the button is enabled.
279 */
280 public boolean getBackButtonEnabled() {
281 return wizardModel.getBackButtonEnabled().booleanValue();
282 }
283
284 /***
285 * Mirrors the WizardModel method of the same name.
286 * @param boolean newValue The new enabled status of the button.
287 */
288 public void setBackButtonEnabled(boolean newValue) {
289 wizardModel.setBackButtonEnabled(new Boolean(newValue));
290 }
291
292 /***
293 * Mirrors the WizardModel method of the same name.
294 * @return A boolean indicating if the button is enabled.
295 */
296 public boolean getNextFinishButtonEnabled() {
297 return wizardModel.getNextFinishButtonEnabled().booleanValue();
298 }
299
300 /***
301 * Mirrors the WizardModel method of the same name.
302 * @param boolean newValue The new enabled status of the button.
303 */
304 public void setNextFinishButtonEnabled(boolean newValue) {
305 wizardModel.setNextFinishButtonEnabled(new Boolean(newValue));
306 }
307
308 /***
309 * Mirrors the WizardModel method of the same name.
310 * @return A boolean indicating if the button is enabled.
311 */
312 public boolean getCancelButtonEnabled() {
313 return wizardModel.getCancelButtonEnabled().booleanValue();
314 }
315
316 /***
317 * Mirrors the WizardModel method of the same name.
318 * @param boolean newValue The new enabled status of the button.
319 */
320 public void setCancelButtonEnabled(boolean newValue) {
321 wizardModel.setCancelButtonEnabled(new Boolean(newValue));
322 }
323
324 /***
325 * Closes the dialog and sets the return code to the integer parameter.
326 * @param code The return code.
327 */
328 void close(int code) {
329 returnCode = code;
330 wizardDialog.dispose();
331 }
332
333 /***
334 * This method initializes the components for the wizard dialog: it creates a JDialog
335 * as a CardLayout panel surrounded by a small amount of space on each side, as well
336 * as three buttons at the bottom.
337 */
338
339 private void initComponents() {
340
341 wizardModel.addPropertyChangeListener(this);
342 wizardController = new WizardController(this);
343
344 wizardDialog.getContentPane().setLayout(new BorderLayout());
345 wizardDialog.addWindowListener(this);
346
347
348
349
350
351
352 JPanel buttonPanel = new JPanel();
353 JSeparator separator = new JSeparator();
354 Box buttonBox = new Box(BoxLayout.X_AXIS);
355
356 cardPanel = new JPanel();
357 cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));
358
359 cardLayout = new CardLayout();
360 cardPanel.setLayout(cardLayout);
361
362 backButton = new JButton(new ImageIcon("com/nexes/wizard/backIcon.gif"));
363 nextButton = new JButton();
364 cancelButton = new JButton();
365
366 backButton.setActionCommand(BACK_BUTTON_ACTION_COMMAND);
367 nextButton.setActionCommand(NEXT_BUTTON_ACTION_COMMAND);
368 cancelButton.setActionCommand(CANCEL_BUTTON_ACTION_COMMAND);
369
370 backButton.addActionListener(wizardController);
371 nextButton.addActionListener(wizardController);
372 cancelButton.addActionListener(wizardController);
373
374
375
376
377
378
379 buttonPanel.setLayout(new BorderLayout());
380 buttonPanel.add(separator, BorderLayout.NORTH);
381
382 buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10)));
383 buttonBox.add(backButton);
384 buttonBox.add(Box.createHorizontalStrut(10));
385 buttonBox.add(nextButton);
386 buttonBox.add(Box.createHorizontalStrut(30));
387 buttonBox.add(cancelButton);
388
389 buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST);
390
391 wizardDialog.getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH);
392 wizardDialog.getContentPane().add(cardPanel, java.awt.BorderLayout.CENTER);
393
394 }
395
396 private static Object getImage(String name) {
397
398 URL url = null;
399
400 try {
401 Class c = Class.forName("com.nexes.wizard.Wizard");
402 url = c.getResource(name);
403 } catch (ClassNotFoundException cnfe) {
404 System.err.println("Unable to find Wizard class");
405 }
406 return url;
407
408 }
409
410 /***
411 * If the user presses the close box on the dialog's window, treat it
412 * as a cancel.
413 * @param WindowEvent The event passed in from AWT.
414 */
415
416 public void windowClosing(WindowEvent e) {
417 returnCode = CANCEL_RETURN_CODE;
418 }
419
420
421
422 static {
423
424 try {
425
426 PropertyResourceBundle resources = (PropertyResourceBundle)
427 ResourceBundle.getBundle("com.nexes.wizard.wizard");
428
429 BACK_TEXT = (String)(resources.getObject("backButtonText"));
430 NEXT_TEXT = (String)(resources.getObject("nextButtonText"));
431 CANCEL_TEXT = (String)(resources.getObject("cancelButtonText"));
432 FINISH_TEXT = (String)(resources.getObject("finishButtonText"));
433
434 BACK_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("backButtonIcon"))));
435 NEXT_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("nextButtonIcon"))));
436 CANCEL_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("cancelButtonIcon"))));
437 FINISH_ICON = new ImageIcon((URL)getImage((String)(resources.getObject("finishButtonIcon"))));
438
439 } catch (MissingResourceException mre) {
440 System.out.println(mre);
441 System.exit(1);
442 }
443 }
444
445 }